package edu.luc.cs413s13.team1.monsterm.model.monster;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import android.util.Log;

import edu.luc.cs413s13.team1.monsterm.model.cell.Cell;
import edu.luc.cs413s13.team1.monsterm.model.cell.CellEvent;
import edu.luc.cs413s13.team1.monsterm.model.common.Constants;
import edu.luc.cs413s13.team1.monsterm.model.common.MonsterStateChangeListener;
import edu.luc.cs413s13.team1.monsterm.model.common.MonsterStateInterface;

public class Monster implements 
	Actor, Runnable, MonsterStateInterface {

	
	  private ExecutorService liveThread;

	  /**
	   * The work thread of this actor.  This thread handles requests, usually
	   * coming from the live thread, to do some work, such as moving to another cell.
	   * It is necessary to use a separate thread for this because an attempt to
	   * enter another cell might block.  For example, without using a separate
	   * work thread, this can cause deadlock
	   * if two actors are trying to move into each other's cell of capacity one.
	   * A separate work thread also allows an actor to change their mind
	   * if another task of higher priority should take precedence over
	   * a task on which the work thread is currently working.
	   */
	  private ExecutorService workThread;

	  /**
	   * The destination of the most recent attempt to move.
	   */
	  private Future task = null;
 
	  /**
	   * This method indicates whether this actor is still alive.
	   */
	  protected synchronized boolean isAlive() { return liveThread != null; }

	  /**
	   * This method brings this actor to life by starting its internal threads.
	   */
	  public synchronized void start() {
	    if (! isAlive()) {
	      liveThread = Executors.newFixedThreadPool(1);
	      workThread = Executors.newFixedThreadPool(1);
	    }
	    liveThread.execute(this);
	  }

	  /**
	   * This method is used to schedule the runnable for execution by this
	   * actor.  If the actor is still waiting for a previously scheduled
	   * runnable to execute, then this invocation preempts the previous one.
	   */
	  protected synchronized void execute(Runnable runnable) {
	    if (task != null && ! task.isDone()) {
	      task.cancel(true);
	    }
	    task = workThread.submit(runnable);
	  }

	private int stateSleepTime = 1000;
	public void setStateSleepTime(int time) {
		this.stateSleepTime = time;
	}
	
	private int moveSleepTime = 1000;
	public void setMoveSleepTime(int time) {
		this.moveSleepTime = time;
	}
	
	public Monster() { 
		this.state = PROTECTED;
	}
	
	public void run() {
		while (! Thread.interrupted()) {
			try {
				//Log.i(Constants.TAG, "run: Entered main thread!");
				// schedule a change of monster state for execution
				//handler.post(changeState);
				// schedule a move for execution
				execute(changeState);
				Thread.sleep(stateSleepTime);
				//handler.post(move);
				execute(move);
				
				Thread.sleep(moveSleepTime);
				//Thread.sleep(100);
				//Thread.sleep(random.nextInt(100));
			} catch (InterruptedException exc) {
				Thread.currentThread().interrupt();
			}
		}
	}

	private static Random random = new Random();

	/**
	 * A runnable representing a move.
	 */
	private Runnable move = new Runnable() {
		public void run() {
			Log.i(Constants.TAG, "run: Entered move thread!");
			try {
				//Thread.sleep(moveSleepTime);
				getCell().randomNeighbor().enter(Monster.this);
				//getCell().leave(Monster.this);
			} catch (InterruptedException e) {
				// if interrupted before entering the cell, then set interrupted flag
				// so that the worker thread can detect this
				Thread.currentThread().interrupt();
			}
		}
	};

	/**
	 * A runnable representing a state change.
	 */
	private Runnable changeState = new Runnable() {
		public void run() {
			//Log.i(Constants.TAG, "run: Entered changeState thread!");
			alternateState();
			//try {
				//Thread.sleep(stateSleepTime);
			/*} catch (InterruptedException e) {
				e.printStackTrace();
			}*/
		}
	};

	/**
	 * This method removes this dead actor from the cell it occupied.
	 */
	protected synchronized void die() {
		Cell cell = getCell();
	    setCell(null);
	    cell.leave(this);
	}

	/**
	 * This method kills this actor by stopping its internal threads.
	 */
	public synchronized boolean kill() {
		if (isAlive()) {
		    //liveThread.shutdown();
		    //workThread.shutdown();
		    //liveThread = null;
		    //workThread = null;
			die();
			return true;
		}
		return false;
	}

	public synchronized void setCell(Cell cell) { currentCell = cell; }
	
	public synchronized Cell getCell() { return currentCell; }

	/**
	 * The cell this actor is currently occupying.
	 */
	private Cell currentCell;

	

	protected void alternateState() {
		
		//Log.i(Constants.TAG, "Alternating state...");
		int n = random.nextInt(2);
		boolean flag = n == 0;
		if (flag) return;
		
		if (state == PROTECTED)
			toVulnerable();
		else
			toProtected();
	}
		
	private final ProtectedState PROTECTED = new ProtectedState(this);
	private final VulnerableState VULNERABLE = new VulnerableState(this);
	
	MonsterState state;
	
	protected void setState(MonsterState state) {
		this.state = state;
		//Log.i(Constants.TAG, "setState: Changing state... to " + state);
	}
	
	private MonsterStateChangeListener monsterStateChageListener;
	  
	public int getStateId() {
		return state.getId();
	}
	
	public boolean isProtected() {
		return state.getId() == Constants.STATE_PROTECTED;
	}
	
	@Override
	public void toVulnerable() {
		setState(VULNERABLE);
	}

	@Override
	public void toProtected() {
		setState(PROTECTED);
	}

	@Override
	public void actionInit() {
		toProtected();
	}

	@Override
	public void onEnterCell(CellEvent event) {
		//Log.w(Constants.TAG, "state change listener: "+monsterStateChageListener);
		monsterStateChageListener.onMonsterStateChange(state.getId());
	}

	public void setMonsterStateChangeListener(
			MonsterStateChangeListener listener) {
		this.monsterStateChageListener = listener;
	}

	@Override
	public void onLeaveCell(CellEvent event) {
		monsterStateChageListener.onMonsterStateChange(state.getId());
		
	}

}
